package com.example.ephuizi.stl.util.photo_3d;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.util.Log;

import com.example.ephuizi.stl.util.STLFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by ephuizi@gmail.com on 2015/10/15.
 */
public class ImageConvert3 {
    List<Triangle> list = new ArrayList<>();

    private class Triangle {

        public Triangle setFirstPoint(float x1, float y1, float z1) {
            this.x1 = x1;
            this.y1 = y1;
            this.z1 = z1;
            return this;
        }

        public Triangle setSecodePoint(float x2, float y2, float z2) {
            this.x2 = x2;
            this.y2 = y2;
            this.z2 = z2;
            return this;
        }

        public Triangle setThridPoint(float x3, float y3, float z3) {
            this.x3 = x3;
            this.y3 = y3;
            this.z3 = z3;
            return this;
        }

        public float[] toFloatArr() {
            float[] arr = new float[9];
            arr[0] = this.x1;
            arr[1] = this.y1;
            arr[2] = this.z1;
            arr[3] = this.x2;
            arr[4] = this.y2;
            arr[5] = this.z2;
            arr[6] = this.x3;
            arr[7] = this.y3;
            arr[8] = this.z3;
            return arr;
        }

        float x1;
        float y1;
        float z1;
        //
        float x2;
        float y2;
        float z2;
        //
        float x3;
        float y3;
        float z3;
    }

    public void convert(String imgPath) {
        list.clear();
        Bitmap img2D = decodeSampledBitmapFromPath(imgPath, 100, 100);

        boolean bitInverted = false;
        int intMinDepth = 0;//图片原本的z最低
        int intImageWidth = img2D.getWidth();
        int intImageHeight = img2D.getHeight();

        short intHeights[][] = new short[intImageWidth + 2][intImageHeight + 2];
        //用于减少重复计算的点
        byte bitTopDone[][] = new byte[intImageWidth + 2][intImageHeight + 2];
        for (int x = 0; x < intImageWidth; x++) {
            for (int y = 0; y < intImageHeight; y++) {
                bitTopDone[x][y] = 0;
            }
        }
        //为了模型封闭,矩阵放宽,所以横列+2
        //calculate iniMinDepth
        //目前印章字是红色,底是白色
        for (int x = 0; x < intImageWidth; x++) {
            for (int y = 0; y < intImageHeight; y++) {
                if (bitInverted) {
                    intHeights[x + 1][y + 1] = (short) Color.red(img2D.getPixel(x, y));
                } else {
                    intHeights[x + 1][y + 1] = (short) (255 - Color.red(img2D.getPixel(x, y)));
                }
                if (intHeights[x + 1][y + 1] < intMinDepth) {
                    intMinDepth = intHeights[x + 1][y + 1];
                }
            }
        }

        if (intMinDepth > 0) {
            //需要添加两个triangle,做模型底面
            //TODO
            Triangle bottom1 = new Triangle();
            bottom1.setFirstPoint(img2D.getWidth() + 1, 0, 0);
            bottom1.setSecodePoint(0, 0, 0);
            bottom1.setThridPoint(0, img2D.getHeight() + 1, 0);
            Triangle bottom2 = new Triangle();
            bottom2.setFirstPoint(img2D.getWidth() + 1, 0, 0);
            bottom2.setSecodePoint(0, img2D.getHeight() + 1, 0);
            bottom2.setThridPoint(img2D.getWidth() + 1, img2D.getHeight() + 1, 0);
            list.add(bottom1);
            list.add(bottom2);
        }


        int intBlockSize = 0;
        //利用color计算高度
        for (int x = 0; x < intImageWidth; x++) {
            for (int y = 0; y < intImageHeight; y++) {
                //这个点还没被计算
                if (bitTopDone[x][y] == 0) {
                    //为了减少计算量,排除没高度的区域
                    if (intHeights[x][y] > 0 || intHeights[x + 1][y] > 0 || intHeights[x][y + 1] > 0 || intHeights[x + 1][y + 1] > 0) {
                        intBlockSize = 0;

                        boolean bitNoMatch = false;
                        //(x,y)点之后的图片最长边
                        int intMatchSize = 0;
                        int intTemp1 = intImageWidth - x;
                        int intTemp2 = intImageHeight - y;
                        if (intTemp1 > intTemp2) {
                            intMatchSize = intTemp1;
                        } else {
                            intMatchSize = intTemp2;
                        }

                        for (int i = 1; i < intMatchSize; i++) {
                            for (int j = 0; j < intMatchSize; j++) {
                                if (intHeights[x][y] != intHeights[x + i][y + j]
                                        || intHeights[x][y] != intHeights[x + j][y + i]) {
                                    bitNoMatch = true;//与前面正方形区域高度不一致
                                    break;
                                }
                            }
                            if (bitNoMatch)
                                break;
                            else
                                intBlockSize = i - 1;//记录高度一致的正方形大小
                        }

                        for (int i = 1; i < intBlockSize; i++) {
                            for (int j = 0; j < intBlockSize; j++) {
                                bitTopDone[x + i][y + j] = 1;//记录被计算的正方形区域
                            }
                        }
                        intBlockSize += 1;//为了模型是密封的,边的高度与区域的高度不一样

                        Triangle objTriangle1 = new Triangle();
                        objTriangle1.setFirstPoint(x, y, intHeights[x][y]);
                        objTriangle1.setSecodePoint(x + intBlockSize, y + intBlockSize, intHeights[x + intBlockSize][y + intBlockSize]);
                        objTriangle1.setThridPoint(x, y + intBlockSize, intHeights[x][y + intBlockSize]);
                        Triangle objTriangle2 = new Triangle();
                        objTriangle2.setFirstPoint(x, y, intHeights[x][y]);
                        objTriangle2.setSecodePoint(x + intBlockSize, y, intHeights[x + intBlockSize][y]);
                        objTriangle2.setThridPoint(x + intBlockSize, y + intBlockSize, intHeights[x + intBlockSize][y + intBlockSize]);
                        list.add(objTriangle1);
                        list.add(objTriangle2);
                    }
                }
            }
        }
    }

    public STLFile toStlFile(String imgPath) {
        this.convert(imgPath);
        ByteBuffer mbb = ByteBuffer.allocateDirect(list.size() * 9 * 4);
        // 数组排列用nativeOrder
        mbb.order(ByteOrder.nativeOrder());
        FloatBuffer fb = mbb.asFloatBuffer();

        for (int i = 0; i < list.size(); i++) {
            Triangle triangle = list.get(i);
            fb.put(triangle.x1);
            fb.put(triangle.y1);
            fb.put(triangle.z1);
            fb.put(triangle.x2);
            fb.put(triangle.y2);
            fb.put(triangle.z2);
            fb.put(triangle.x3);
            fb.put(triangle.y3);
            fb.put(triangle.z3);
        }
        fb.position(0);
        //TODO
        mbb = ByteBuffer.allocateDirect(list.size() * 9 * 4);
        // 数组排列用nativeOrder
        mbb.order(ByteOrder.nativeOrder());
        FloatBuffer test = mbb.asFloatBuffer();
        return new STLFile(list.size(), new float[]{150, 150, 150}, new float[]{-1, -1, 0}, fb, test);
    }

    //
    //google-教材推荐的加载方式
    //
    public static Bitmap decodeSampledBitmapFromPath(String imgPath, int reqWidth, int reqHeight) {

        FileInputStream is = null;
        final BitmapFactory.Options options = new BitmapFactory.Options();
        try {
            File img = new File(imgPath);
            is = new FileInputStream(img);
            // First decode with inJustDecodeBounds=true to check dimensions
            //note: allowing the caller to query the bitmap without having to allocate the memory for its pixels.
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, options);
            is.close();
            is = new FileInputStream(img);
            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
        } catch (FileNotFoundException e) {
            Log.e("info", e.getMessage());
        } catch (IOException e) {
            Log.e("info", e.getMessage());
        }
        return BitmapFactory.decodeStream(is, null, options);
    }

    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
                //A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the inSampleSize documentation.
            }
        }

        return inSampleSize;
    }


}
